#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/devinfo.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/list.h>
#include <linux/printk.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/spinlock.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/of_device.h>
#include <linux/acpi.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/usb.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/hid.h>


// struct input_event 结构体是应用层获取的数据原理

#define MISC_USB_MOUSE_DEV_NAME "misc_usb_mouse"


struct jz2440_mosue_inf {
    char name[128];      /* 鼠标设备的名称，包括生产厂商、产品类别、产品等信息 */
    char phys[64];       /* 设备节点名称 */
    /* usb 鼠标是一种 usb设备，需要内嵌一个usb设备结构体来描述其usb属性 */
    struct usb_device *usbdev;
    /** urb请求包结构体，用于传送数据 */
    struct urb *urb;

    /** 虚拟缓冲区地址 */
    char *data;
    /** DMA缓冲区 */
    dma_addr_t data_dma;

    int bInterval; /* 中断传输的频率 */
    int pipe;      /* 传输的管道 */
    int maxp;      /* 传输的最大字节数 */

    wait_queue_head_t wait_queue;  /* 等待队列 */
    int mouse_data_flag;

    char buf[8];   /* copy user buf */
};

/* usb鼠标设备,全局指针 */
static struct jz2440_mosue_inf *p_mouse = NULL;

static void usb_complete_mouse(struct urb *urb)
{
    /* 私有数据 */
    struct jz2440_mosue_inf *mouse = urb->context;
    /* 接收到的数据 */
    char *data = mouse->data;
    int status = 0;
    int data_len = mouse->maxp > 8 ? 8 : mouse->maxp;

    /* urb的当前状态 */
    switch (urb->status) {
        case 0:                 /* success */
            break;
        case -ECONNRESET:       /* unlink */
        case -ENOENT:
        case -ESHUTDOWN:
        return;
        /* -EPIPE:  should clear the halt */
        default:                /* error */
            goto resubmit;
    }
    
    /* 复制数据给copy_to_user */
    memset(mouse->buf, 0, sizeof(mouse->buf));
    memcpy(mouse->buf, data, data_len);

    /* 唤醒read睡眠函数 */
    mouse->mouse_data_flag = 1;
    wake_up_interruptible(&mouse->wait_queue);

    /*
     * 系统需要周期性不断地获取鼠标的事件信息，因此在 urb 回调函数的末尾再次提交urb请求块，这样又会调用新的回调函数，周而复始。
     * 在回调函数中提交urb一定只能是 GFP_ATOMIC 优先级的，因为 urb 回调函数运行于中断上下文中，在提
     * 交 urb 过程中可能会需要申请内存、保持信号量，这些操作或许会导致 USB core 睡眠，一切导致睡眠的行
     * 为都是不允许的。
     */
resubmit:
        status = usb_submit_urb(urb, GFP_ATOMIC);
        if (status)
            dev_err(&mouse->usbdev->dev,
                    "can't resubmit intr, %s-%s/input0, status %d\n",
                    mouse->usbdev->bus->bus_name,
                    mouse->usbdev->devpath, status);
    
}

static int jz_usb_mouse_open(struct inode *inode, struct file *file)
{
    struct jz2440_mosue_inf *mouse = p_mouse;
    struct urb *urb = NULL;

    /* 设置file的私有数据 */
    file->private_data = mouse;

    /* 初始化等待队列 */
    init_waitqueue_head(&mouse->wait_queue);
    mouse->mouse_data_flag = 0;

    /* 分配一个urb数据结构体 */
    urb = usb_alloc_urb(0, GFP_KERNEL);
    mouse->urb = urb;

    /* 填充urb */
    usb_fill_int_urb(urb,
                     mouse->usbdev,
                     mouse->pipe,
                     mouse->data,
                     (mouse->maxp > 8 ? 8 : mouse->maxp),
                     usb_complete_mouse,
                     mouse,
                     mouse->bInterval);

    /* 支持DMA,所以要告诉urb结构体,已经使用了DMA缓冲区地址 */
    urb->transfer_dma = mouse->data_dma;
    urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    
    /* 使用usb_submit_urb提交urb */
    if (usb_submit_urb(mouse->urb, GFP_KERNEL))
        return -EIO;
   
    return 0;
}

static ssize_t jz_usb_mouse_read(struct file *file, char __user *bufs, size_t size, loff_t *lf)
{
    struct jz2440_mosue_inf *mouse = file->private_data;
    int ret = 0;

    /* 获取数据 */

    if (!mouse->mouse_data_flag) {
        wait_event_interruptible(mouse->wait_queue, mouse->mouse_data_flag);
    }
    
    ret = copy_to_user(bufs, mouse->buf, size); /* 实际未拷贝的字节数据 */
    if (ret) {
        // 重新设置flags
        mouse->mouse_data_flag = 0;
        return size-ret;
    }

    // 重新设置flags
    mouse->mouse_data_flag = 0;
    
    return size; /*返回实际拷贝的大小*/
}

static int jz_usb_mouse_release(struct inode *inode, struct file *file)
{

    struct jz2440_mosue_inf *mouse = file->private_data;

    /* 杀掉提交到内核中的urb */
    usb_kill_urb(mouse->urb);

    /* 释放urb */
    usb_free_urb(mouse->urb);

    return 0;
}

static struct file_operations misc_usb_mouse_fops = {
    .owner  =   THIS_MODULE,
    .open   =   jz_usb_mouse_open,
    .read   =   jz_usb_mouse_read,
    .release =  jz_usb_mouse_release,
};


/**
 * misc驱动 
*/
static struct miscdevice misc_dev = {
    .fops  =  &misc_usb_mouse_fops,        
    .minor =  MISC_DYNAMIC_MINOR,  // 255自动的动态分配                   
    .name  =  MISC_USB_MOUSE_DEV_NAME,
    .mode  =  0755,
};


static const struct usb_device_id jz_device_table[] = {
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
        USB_INTERFACE_PROTOCOL_MOUSE), .driver_info = (kernel_ulong_t)"jz2440 mouse", },
	{}
};

MODULE_DEVICE_TABLE(usb, jz_device_table);

/**
 * 每一个usb接口驱动和usb接口匹配成功之后，会调用probe函数, 参考hub_probe实现
 */
static int jz_usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    /* 获取usb设备 */
    struct usb_device *dev = interface_to_usbdev(intf);
    /* 当前激活的接口配置 */
    struct usb_host_interface *interface = NULL;
    /* 当前usb接口下的端点0的端点描述符 */
    struct usb_endpoint_descriptor *endpoint = NULL;
    /* usb鼠标设备,全局变量,多个鼠标存在问题 */
    struct jz2440_mosue_inf *mouse = NULL;
    /* 端点管道 */
    int pipe = 0;
    int maxp = 0;

    /* 记录当前激活的接口配置 */
    interface = intf->cur_altsetting;

    /* 判断是否是鼠标 */
    /* 端点个数，鼠标只有1个 */
    if (interface->desc.bNumEndpoints != 1)
          return -ENODEV;

    /* 第一次插入usb设备时,会使用接口下的端点0的端点描述符 */
    endpoint = &interface->endpoint[0].desc;
    if (!usb_endpoint_is_int_in(endpoint))
          return -ENODEV;

    /* 输出鼠标的PID和VID */
    dev_err(NULL, "PID = %d VID = %d\n", dev->descriptor.idVendor, dev->descriptor.idProduct);

    /* 创建每个端点对应的PIPE进行通讯 */
    /* 中断类型usb_rcvintpipe/usb_sndintpipe */
    /* 控制类型usb_sndctrlpipe/usb_rcvctrlpipe */
    /* 实时类型usb_sndisocpipe/usb_sndisocpipe */
    /* 批量类型usb_sndbulkpipe/usb_rcvbulkpipe */

    /* 鼠标是中断类型 dev: usb_device设备结构体 端点描述符的成员endpoint->bEndpointAddress */
    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

    /* 获取本端点接受或发送的最大信息包的大小 */
    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

    /* 分配USB的数据的结构体 */
    mouse = devm_kzalloc(&intf->dev, sizeof(struct jz2440_mosue_inf), GFP_KERNEL);
    if (!mouse)
		return -ENOMEM;

    p_mouse = mouse;
    // 初始化保存usb信息
    mouse->usbdev = dev;
    mouse->bInterval = endpoint->bInterval;
    mouse->pipe = pipe;
    mouse->maxp = maxp;
    
    /* USB控制器需要一块物理上连续的地址空间进行首发数据,因此使用DMA传输数据 */
    /**
     * 申请内存空间用于数据传输，data 为指向该空间的地址，data_dma 则是这块内存空间的 dma 映射，
     * 即这块内存空间对应的 dma 地址。在使用 dma 传输的情况下，则使用 data_dma 指向的 dma 区域，
     * 否则使用 data 指向的普通内存区域进行传输。
     * GFP_ATOMIC 表示不等待，GFP_KERNEL 是普通的优先级，可以睡眠等待，由于鼠标使用中断传输方式，
     * 不允许睡眠状态，data 又是周期性获取鼠标事件的存储区，因此使用 GFP_ATOMIC 优先级，如果不能
     * 分配到内存则立即返回 0。
     */
    mouse->data = usb_alloc_coherent(dev, maxp, GFP_ATOMIC, &mouse->data_dma);
    if (!mouse->data)
       return -ENOMEM;

    /* 获取鼠标的名字 */
    if (dev->manufacturer)
        strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));

    /* 拼接名字 */
    if (dev->product) {
        if(dev->manufacturer)
            strlcat(mouse->name, " ", sizeof(mouse->name));
        strlcat(mouse->name, dev->product, sizeof(mouse->name));
    }

    /* 如果鼠标名没有 */
    if (!strlen(mouse->name))
        snprintf(mouse->name, sizeof(mouse->name),
                 "USB HIDBP Mouse %04x:%04x",
                 le16_to_cpu(dev->descriptor.idVendor),
                 le16_to_cpu(dev->descriptor.idProduct));
    /*
    * 填充鼠标设备结构体中的节点名。usb_make_path 用来获取 usb设备在 sysfs 中的路径，格式为：usb-usb 总线号-路径名。
    */
    usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
    strlcat(mouse->phys, "/input0", sizeof(mouse->phys));

    /* 保存usb私有数据 */
    usb_set_intfdata(intf, mouse);

    /* 创建MSIC设备 */
    misc_register(&misc_dev);    
    
    return 0;
}

static void jz_usb_mouse_disconnect(struct usb_interface *intf)
{
    /* 获取intf->dev->driver_data */
    struct jz2440_mosue_inf *mouse = usb_get_intfdata(intf);
    /* 释放usb缓存区 */
    usb_free_coherent(mouse->usbdev, (mouse->maxp > 8 ? 8 : mouse->maxp), mouse->data, mouse->data_dma);

    /* 释放掉私有数据指向 */
    usb_set_intfdata(intf, NULL);

    /* 释放MSIC设备 */
    misc_deregister(&misc_dev);

    return;
}

/* USB设备不需要设备树,需要知道PID和VID */
static struct usb_driver jz_usb_mouse_driver = {
    .name = "usb_093a_mouse",
    .probe = jz_usb_mouse_probe,
    .disconnect = jz_usb_mouse_disconnect,
    .id_table = jz_device_table,
};

/*
 * init入口函数
*/
static int usb_mouse_drv_init(void)
{
    int ret = 0;
    
    ret = usb_register(&jz_usb_mouse_driver);
    if (ret)
        dev_err(NULL, "usb_mouse_drv_init failed\n");
    
    return ret;
}

/*
 * exit出口函数
 */
static void usb_mouse_drv_exit(void)
{
    usb_deregister(&jz_usb_mouse_driver);

    return;
}

module_init(usb_mouse_drv_init);
module_exit(usb_mouse_drv_exit);

/* 常规其他的写法 */
// 参考驱动usbmouse.c
// module_usb_driver(usb_mouse_driver);


MODULE_AUTHOR("huang");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("usb mouse driver");


